import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'

import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'

import Stats from 'three/examples/jsm/libs/stats.module'

import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js'
import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass.js'
import { ShaderPass } from 'three/examples/jsm/postprocessing/ShaderPass.js'
import { OutlinePass } from 'three/examples/jsm/postprocessing/OutlinePass.js'
import { OutputPass } from 'three/examples/jsm/postprocessing/OutputPass'
import { FXAAShader } from 'three/examples/jsm/shaders/FXAAShader.js'

import { CSS2DRenderer, CSS2DObject } from 'three/examples/jsm/renderers/CSS2DRenderer'

export default class ThreeBase {
  initScene () {
    this.scene = new THREE.Scene()
  }

  initCamera () {
    this.camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000)
    this.camera.position.set(0, 30, 0)
    this.camera.lookAt(0, 0, 0)
  }

  initRenderer () {
    this.renderer = new THREE.WebGLRenderer()
    this.renderer.setSize(window.innerWidth, window.innerHeight)
  }

  initLight () {
    // 初始化光源
    const ambient = new THREE.AmbientLight(0xffffff, 1) // 环境光
    const light = new THREE.PointLight(0xffffff, 1) // 点光
    // 添加光源
    this.scene.add(ambient)
    light.position.set(200, 200, 400)
    this.scene.add(light)
  }

  initControls () {
    // 初始化控制器
    this.controls = new OrbitControls(this.camera, this.renderer.domElement)
    this.controls.minPolarAngle = 0
    this.controls.maxPolarAngle = Math.PI * 0.48
    // 绑定控制器（更新渲染器）
    this.controls.addEventListener('change', () => {
      this.renderer.render(this.scene, this.camera)
    })
  }

  initAxesHelper () {
    // 初始化坐标系
    const axesHelper = new THREE.AxesHelper(200)
    // 添加坐标系
    this.scene.add(axesHelper)
  }

  initFloor () {
    const textLoader = new THREE.TextureLoader()
    const geometry = new THREE.PlaneGeometry(1920, 1920)
    const texture = textLoader.load('/src/assets/images/floor.png')
    const material = new THREE.MeshBasicMaterial({
      map: texture,
      transparent: true,
      opacity: 0.2
    })
    console.log(material)
    texture.wrapS = THREE.RepeatWrapping
    texture.wrapT = THREE.RepeatWrapping

    texture.repeat.x = 500
    texture.repeat.y = 500
    const mesh = new THREE.Mesh(geometry, material)
    mesh.rotateX(-Math.PI / 2)
    this.scene.add(mesh)
  }

  initComposer () {
    this.composer = new EffectComposer(this.renderer)

    const renderPass = new RenderPass(this.scene, this.camera)
    this.composer.addPass(renderPass)

    this.outlinePass = new OutlinePass(
      new THREE.Vector2(window.innerWidth, window.innerHeight),
      this.scene,
      this.camera
    )
    this.composer.addPass(this.outlinePass)

    const outputPass = new OutputPass()
    this.composer.addPass(outputPass)

    const effectFXAA = new ShaderPass(FXAAShader)
    effectFXAA.uniforms.resolution.value.set(1 / window.innerWidth, 1 / window.innerHeight)
    this.composer.addPass(effectFXAA)

    this.outlinePass.visibleEdgeColor.set('red')
  }

  initEvent (fn) {
    return new Promise((resolve, reject) => {
      // 绑定点击事件
      const raycaster = new THREE.Raycaster()
      const mouse = new THREE.Vector2()

      this.clickEvent = (e) => {
        // 计算鼠标点击位置
        e.preventDefault()

        // 转换为webgl坐标系下的鼠标位置
        mouse.x = (e.clientX / window.innerWidth) * 2 - 1
        mouse.y = -(e.clientY / window.innerHeight) * 2 + 1

        // 从相机发射一条经过鼠标点击位置的射线
        raycaster.setFromCamera(mouse, this.camera)

        // 计算射线和模型的交点
        const intersects = raycaster.intersectObjects(this.scene.children, true)

        // 如果有交点，则执行相应操作
        console.log(intersects)
        if (
          intersects.length &&
          intersects[0].object.type === 'Mesh' &&
          this.activeObj?.object?.name !== intersects[0].object.name
        ) {
          this.activeObj = intersects[0]
          this.outlinePass.selectedObjects = [intersects[0].object]
          intersects[0].object.add(this.labelObj)
          console.log(intersects[0].object.position)
          this.camera.lookAt(1, 1, 1)
          resolve(intersects[0])
        } else {
          this.activeObj?.object.remove(this.labelObj)
          this.activeObj = null
          this.outlinePass.selectedObjects = []
        }
      }
      this.renderer.domElement.addEventListener('mousedown', this.clickEvent)
    })
  }

  disposeEvent () {
    this.renderer.domElement.removeEventListener('mousedown', this.clickEvent)
  }

  initLabel () {
    const labelDom = document.getElementById('bimLabel')
    this.labelObj = new CSS2DObject(labelDom)
    this.labelObj.position.set(-4, 4, -4)

    const geometry = new THREE.BufferGeometry()
    geometry.setFromPoints([new THREE.Vector3(0, 0, 0), new THREE.Vector3(4, -4, 4)])
    const material = new THREE.LineBasicMaterial({
      color: 0x11a8cd,
      opacity: 0.5
    })
    const line = new THREE.Line(geometry, material)
    this.labelObj.add(line)

    this.label = new CSS2DRenderer()
    this.label.render(this.scene, this.camera)
    this.label.setSize(window.innerWidth, window.innerHeight)
    this.label.domElement.style.pointerEvents = 'none'
    this.label.domElement.style.position = 'absolute'
    this.label.domElement.style.top = '0px'
    this.label.domElement.style.left = '0px'
    document.body.appendChild(this.label.domElement)
  }

  animate () {
    requestAnimationFrame(this.animate.bind(this))
    this.controls.update()
    // this.label.render(this.scene, this.camera)
    this.composer.render()
    this.stats.update()
  }

  initStats () {
    this.stats = new Stats()
    document.getElementById(this.id)?.appendChild(this.stats.domElement)
  }

  init () {
    this.initScene()
    this.initRenderer()
    this.initCamera()
    this.initControls()
    this.initLight()
    this.initFloor()
    this.initComposer()
    this.initAxesHelper()
    window.addEventListener('resize', this.initResize)
  }

  load (url) {
    // 加载模型
    const gltfLoader = new GLTFLoader()
    gltfLoader.load(url, (data) => {
      console.log(data)
      this.models = data
      this.scene.add(data.scene)
      this.renderer.render(this.scene, this.camera)
    })
  }

  render () {
    document.getElementById(this.id)?.appendChild(this.renderer.domElement)
    // this.initLabel()
    this.initStats()
    this.animate()
  }

  initResize () {
    this.renderer.setSize(window.innerWidth, window.innerHeight)
    // this.label.setSize(window.innerWidth, window.innerHeight)
    this.camera.aspect = window.innerWidth / window.innerHeight
    this.camera.updateProjectionMatrix()
  }

  dispose () {
    window.removeEventListener('resize', this.initResize())
  }

  constructor (id) {
    this.id = id
    this.init()
  }
}
